﻿using System.Diagnostics.CodeAnalysis;

using Microsoft.Extensions.DependencyInjection;

using zijian666.Core;
using zijian666.DI.Features;

namespace zijian666.DI;

/// <summary>
/// 标记'组件', 调用 <seealso cref="ConfigureServicesExtensions.AddComponents(IServiceCollection)"/> 后自动输入所有标记的类型
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class ComponentAttribute() : Attribute
{
    /// <summary>
    /// 注入特定类型的服务
    /// </summary>
    /// <param name="serviceType">注入服务的类型</param>
    public ComponentAttribute(Type serviceType) : this()
        => ServiceType = serviceType ?? throw new ArgumentNullException(nameof(serviceType));
    /// <summary>
    /// 注入特定类型的服务
    /// </summary>
    /// <param name="key">服务键</param>
    public ComponentAttribute(object key) : this()
        => Key = key ?? throw new ArgumentNullException(nameof(key));

    /// <summary>
    /// 注入特定类型的服务
    /// </summary>
    /// <param name="key">服务键</param>
    /// <param name="serviceType">注入服务的类型</param>
    public ComponentAttribute(object key, Type serviceType) : this(serviceType)
        => Key = key ?? throw new ArgumentNullException(nameof(key));

    /// <summary>
    /// 注入服务的类型,如果为空则注入当前类型
    /// </summary>
    public Type? ServiceType { get; private set; }

    /// <summary>
    /// 服务键
    /// </summary>
    public object? Key { get; set; }

    /// <summary>
    /// 注入服务的生命周期
    /// </summary>
    public virtual ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Singleton;

    internal ComponentAttribute Clone(Type serviceType)
    {
        ArgumentNullException.ThrowIfNull(serviceType);
        var clone = (ComponentAttribute)MemberwiseClone();
        clone.ServiceType = serviceType;
        return clone;
    }

    private IKeyedServiceFeature KeyedService
        => FeatureManager.Get<IKeyedServiceFeature>()
        ?? throw new NotSupportedException("当前环境不支持键值服务特性, 请先安装 zijian666.DI.Net8 或自行实现 IKeyedServiceFeature 接口");

    public virtual void ConfigureServices(IServiceCollection services, Type implementationType, bool added)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentNullException.ThrowIfNull(implementationType);

        var serviceType = ServiceType ?? implementationType;
        // 如果已经存在服务描述, 则创建一个代理服务描述
        if (added)
        {
            // 避免重复注入
            if (serviceType == implementationType)
            {
                return;
            }
            // 创建一个代理服务描述, 通过工厂方法获取实际的服务实例
            var proxy = Key is not null
                ? KeyedService.CreateKeyedServiceDescriptor(serviceType, Key, (p, key) => KeyedService.GetKeyedService(p, implementationType, Key), Lifetime)
                : new ServiceDescriptor(serviceType, p => p.GetService(implementationType), Lifetime);
            if (!services.Any(x => proxy.Equals(x)))
            {
                services.Add(proxy);
            }
            return;

        }

        // 如果没有服务描述, 则创建一个新的服务描述
        var desc = Key is not null
            ? KeyedService.CreateKeyedServiceDescriptor(serviceType, Key, implementationType, Lifetime)
            : new ComponentServiceDescriptor(serviceType, implementationType, Lifetime);
        if (!services.Any(x => desc.Equals(x)))
        {
            services.Add(desc);
        }
    }
}


internal class ComponentServiceDescriptor : ServiceDescriptor
{
    public ComponentServiceDescriptor(Type serviceType, object instance) : base(serviceType, instance)
    {
    }

    public ComponentServiceDescriptor(Type serviceType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType, ServiceLifetime lifetime) : base(serviceType, implementationType, lifetime)
    {
    }

    public ComponentServiceDescriptor(Type serviceType, Func<IServiceProvider, object> factory, ServiceLifetime lifetime) : base(serviceType, factory, lifetime)
    {
    }

    public override bool Equals(object? obj)
    {
        if (obj is not ComponentServiceDescriptor other)
        {
            return false;
        }
        return ServiceType == other.ServiceType
            && ImplementationType == other.ImplementationType
            && ImplementationInstance == other.ImplementationInstance
            && ImplementationFactory == other.ImplementationFactory
            && Lifetime == other.Lifetime;
    }
}

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class ScopedComponentAttribute : ComponentAttribute
{
    public ScopedComponentAttribute()
    {
    }

    public ScopedComponentAttribute(Type serviceType) : base(serviceType)
    {
    }

    public ScopedComponentAttribute(object key) : base(key)
    {
    }

    public ScopedComponentAttribute(object key, Type serviceType) : base(key, serviceType)
    {
    }

    public override ServiceLifetime Lifetime
    {
        get => ServiceLifetime.Scoped;
        set => throw new NotSupportedException();
    }
}


[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class TransientComponentAttribute : ComponentAttribute
{
    public TransientComponentAttribute()
    {
    }
    public TransientComponentAttribute(Type serviceType) : base(serviceType)
    {
    }
    public TransientComponentAttribute(object key) : base(key)
    {
    }
    public TransientComponentAttribute(object key, Type serviceType) : base(key, serviceType)
    {
    }
    public override ServiceLifetime Lifetime
    {
        get => ServiceLifetime.Transient;
        set => throw new NotSupportedException();
    }
}

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class SingletonComponentAttribute : ComponentAttribute
{
    public SingletonComponentAttribute()
    {
    }
    public SingletonComponentAttribute(Type serviceType) : base(serviceType)
    {
    }
    public SingletonComponentAttribute(object key) : base(key)
    {
    }
    public SingletonComponentAttribute(object key, Type serviceType) : base(key, serviceType)
    {
    }
    public override ServiceLifetime Lifetime
    {
        get => ServiceLifetime.Singleton;
        set => throw new NotSupportedException();
    }
}
